home *** CD-ROM | disk | FTP | other *** search
- /*
- File: MewLaw.c
-
- Contains: Sample sound decompression component to convert
- µLaw-encoded data into 16-bit linear samples
-
- Written by: Kip Olson
-
- Copyright: © 1994 by Apple Computer, Inc.
- */
-
- #include <Memory.h>
- #include <Errors.h>
- #include <SoundInput.h>
- #include <Components.h>
- #include <GestaltEqu.h>
-
- #include "Sound.h"
- #include "SoundComponents.h"
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Constants
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #define kDelegateComponentCall ((ComponentRoutine) -1L) // flag that selector should be delegated instead of called
-
- #define kMewLawVersion 0x00010000 // version for this sound component
- #define kRequiredSndMgrMajorRev 3 // Sound Manager version required to run this component
-
- #define kOutputSampleFormat 'twos' // output sample format
- #define kOutputSampleSize 16 // output sample size
- #define kMaxOutputSamples 1024 // max no. samples in output buffer
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Data Structures
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* Sound component globals */
-
- typedef struct {
-
- // these are general purpose variables that every sound component will need
- ComponentInstance sourceComponent; // component to call when we need more data
- SoundComponentDataPtr sourceDataPtr; // pointer to source data structure
- SoundComponentData thisComponent; // description of this component's output
- Handle globalsHandle; // handle to component globals
- short outputFrames; // max no. frames in output buffer
- short outputSamples; // max no. samples in output buffer
- CompressionInfo compInfo; // info about compressor
- Boolean reverse; // true if data should be played back in reverse
- Boolean pad1;
- short buffer[kMaxOutputSamples * 2]; // room for 16-bit, stereo samples
- } GlobalsRecord, *GlobalsPtr;
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Prototypes
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- #ifdef THINK_C
- pascal ComponentResult main(ComponentParameters *params, GlobalsPtr globals);
- #else
- pascal ComponentResult MewLaw(ComponentParameters *params, GlobalsPtr globals);
- #endif
-
- ComponentRoutine GetComponentRoutine(short selector);
- pascal ComponentResult __ComponentRegister(void *unused1);
- pascal ComponentResult __ComponentVersion(void *unused1);
- pascal ComponentResult __ComponentCanDo(void *unused1, short selector);
- pascal ComponentResult __ComponentOpen(void *unused1, ComponentInstance self);
- pascal ComponentResult __ComponentClose(GlobalsPtr globals, ComponentInstance self);
- pascal ComponentResult __SetSource(GlobalsPtr globals, SoundSource sourceID, ComponentInstance source);
- pascal ComponentResult __SetOutput(GlobalsPtr globals, SoundComponentDataPtr requested,
- SoundComponentDataPtr *actual);
- pascal ComponentResult __GetInfo(GlobalsPtr globals, SoundSource sourceID, OSType selector, void *infoPtr);
- pascal ComponentResult __StopSource(GlobalsPtr globals, short count, SoundSource *sources);
- pascal ComponentResult __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb,
- long actions);
- pascal ComponentResult __GetSourceData(GlobalsPtr globals, SoundComponentDataPtr *resultDataPtr);
- ComponentResult PrimeSource(GlobalsPtr globals);
- void InitializeDecompressor(GlobalsPtr globals);
- void GetCompressorInfo(CompressionInfoPtr cp);
- void DecompressULaw(Byte *inbuf, short *outbuf, unsigned long framesToConvert,
- unsigned long numChannels, unsigned long whichChannel);
-
- // Think C will use A4 to reference globals and tables, so we need to
- // save, setup and restore A4 in our main loop. The following are some
- // handy inlines for dealing with A4.
-
- #ifdef THINK_C
-
- #pragma parameter SetRegisterA4(__A0)
- void SetRegisterA4(void *a4) = { 0x2848 }; /* move.l a0,a4 */
- void* GetRegisterA4(void) = { 0x200C }; /* move.l a4,d0 */
-
- #endif
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Main Component Entry Point
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* =======================================================================================
- MewLaw
-
- The function of this routine is to dispatch to the appropriate component method. It first
- calls finds the address of the method to dispatch to using the selector provided in the
- what field of the parameter block. If the address is -1L, then this selector should
- be delegated. If the address is nil, this selector is not supported.
- ======================================================================================= */
-
- #ifdef THINK_C
- pascal ComponentResult main(ComponentParameters *params, GlobalsPtr globals)
- #else
- pascal ComponentResult MewLaw(ComponentParameters *params, GlobalsPtr globals)
- #endif
- {
- ComponentRoutine theRtn;
- ComponentResult result;
-
- #ifdef THINK_C
- void *savedA4;
-
- savedA4 = GetRegisterA4();
- SetRegisterA4(main);
- #endif
-
- theRtn = GetComponentRoutine(params->what); // get address of component routine
-
- if (theRtn == nil) // selector not implemented
- result = badComponentSelector;
- else if (theRtn == kDelegateComponentCall) // selector should be delegated
- result = DelegateComponentCall(params, globals->sourceComponent);
- else
- result = CallComponentFunctionWithStorage((Handle) globals, params, (ComponentFunctionUPP) theRtn);
-
- #ifdef THINK_C
- SetRegisterA4(savedA4);
- #endif
-
- return (result);
-
- }
-
- /* =======================================================================================
- GetComponentRoutine
-
- The function of this routine is to return the address of the appropriate component method.
- To do this, the routine must deal with 3 selector ranges:
-
- -5 to -1 These are the standard Component Manager selectors that all components
- must share. Refer to the Component Manager documentation for more info.
-
- 0 to 255 These selectors cannot be delegated. If the sound component does not implement
- one of these selectors, it should return the badComponentSelector error.
-
- 256 to ∞ These selectors should be delegated. If the sound component does not implement
- one of these selectors, it should use DelegateComponentCall() to pass
- this selector on up the chain. If the sound component does implement this
- selector, it should first delegate the selector, then perform the function.
- ======================================================================================= */
-
- ComponentRoutine GetComponentRoutine(short selector)
- {
- void *theRtn;
-
- if (selector < 0)
- switch (selector) // standard component selectors
- {
- case kComponentRegisterSelect:
- theRtn = __ComponentRegister;
- break;
-
- case kComponentVersionSelect:
- theRtn = __ComponentVersion;
- break;
-
- case kComponentCanDoSelect:
- theRtn = __ComponentCanDo;
- break;
-
- case kComponentCloseSelect:
- theRtn = __ComponentClose;
- break;
-
- case kComponentOpenSelect:
- theRtn = __ComponentOpen;
- break;
-
- default:
- theRtn = nil; // unknown selector, so fail
- break;
- }
- else if (selector < kDelegatedSoundComponentSelectors) // selectors that cannot be delegated
- switch (selector)
- {
- case kSoundComponentSetSourceSelect:
- theRtn = __SetSource;
- break;
-
- case kSoundComponentGetSourceDataSelect:
- theRtn = __GetSourceData;
- break;
-
- case kSoundComponentSetOutputSelect:
- theRtn = __SetOutput;
- break;
-
- default:
- theRtn = nil; // unknown selector, so fail
- break;
- }
- else // selectors that can be delegated
- switch (selector)
- {
- case kSoundComponentGetInfoSelect:
- theRtn = __GetInfo;
- break;
-
- case kSoundComponentStopSourceSelect:
- theRtn = __StopSource;
- break;
-
- case kSoundComponentPlaySourceBufferSelect:
- theRtn = __PlaySourceBuffer;
- break;
-
- default:
- theRtn = kDelegateComponentCall; // unknown selector, so delegate it
- break;
- }
-
- return (theRtn);
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Component Manager Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-
- /* ==============================================================================
- Component Register
-
- This routine is called once, usually at boot time, when the Component Manager
- is first registering this sound component. This routine should check to see if the proper
- Sound Manager is installed and return 0 if it is. If the right Sound Manager is not
- installed, the routine should return 1 and this component will not be registered.
-
- NOTE: The cmpWantsRegisterMessage bit must be set in the component flags of the
- sound component in order for this routine to be called.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentRegister(void *unused1)
- {
- #pragma unused (unused1)
-
- long result;
- NumVersion version;
-
- if ((Gestalt(gestaltSoundAttr, &result) == noErr) && // snd gestalt is available
- (result & (1L << gestaltSoundIOMgrPresent))) // snd dispatcher is available
- {
- version = SndSoundManagerVersion(); // get the Sound Manager version
- if (version.majorRev >= kRequiredSndMgrMajorRev) // it's what we need
- {
- return (0); // install this decompression component
- }
- }
-
- return (1); // do not install component
- }
-
- /* ==============================================================================
- Component Version
-
- This routine is called to determine the current version of the decompression component.
- It should return a fixed-point value containing the version, like 0x10001
- for version 1.1. The version given here must match the version stored in
- the 'thng' resource.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentVersion(void *unused1)
- {
- #pragma unused (unused1)
-
- return (kMewLawVersion); // return sound component version
- }
-
- /* ==============================================================================
- Component CanDo
-
- This routine is called to determine if a particular selector is implemented.
- It should return 1 if this is a valid selector, or 0 if the selector is not
- implemented or if the selector is always delegated.
-
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentCanDo(void *unused1, short selector)
- {
- #pragma unused (unused1)
-
- ComponentRoutine theRtn;
-
- theRtn = GetComponentRoutine(selector); // see if this selector is implemented
-
- if ((theRtn == nil) || // selector is not implemented
- (theRtn == kDelegateComponentCall)) // or selector is always delegated
- return (0); // no can do
- else
- return (1); // selector is implemented
- }
-
- /* ==============================================================================
- Component Open
-
- This routine is called when the Component Manager creates an instance of this
- component. The routine should allocate global variables in the appropriate heap
- and call SetComponentInstanceStorage() so the Component Manager can remember
- the globals and pass them to all the method calls.
-
- Determining the heap to use can be tricky. The Component Manager will normally
- load the component code into the system heap, which is good, since many applications
- will be sharing this component to play sound. In this case, the components's global
- variable storage should also be created in the system heap.
-
- However, if system heap memory is tight, the Component Manager will load
- the component into the application heap of the first application that plays sound.
- When this happens, the component should create global storage in the application heap
- instead. The Sound Manager will make sure that other applications will not try
- to play sound while the component is in this application heap.
-
- To determine the proper heap to use, call GetComponentInstanceA5(). If the value
- returned is 0, then the component was loaded into the system heap, and all storage
- should be allocated there. If the value returned is non-zero, the component is in
- the application heap specifed by returned A5 value, and all storage should be
- allocated in this application heap.
-
- NOTE: If the component is loaded into the application heap, the value returned by
- GetComponentRefCon() will be 0.
- NOTE: Do not attempt to initialize in this call, since the Component Manager will
- call Open() BEFORE calling Register().
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentOpen(void *unused1, ComponentInstance self)
- {
- #pragma unused (unused1)
-
- Handle h;
- GlobalsPtr globals;
-
- h = NewHandleClear(sizeof(GlobalsRecord)); // get space for globals
- if (h == nil)
- return(MemError());
-
- HLock(h);
- globals = (GlobalsPtr) *h;
- SetComponentInstanceStorage (self, (Handle) globals); // save pointer to our globals
-
- globals->globalsHandle = h; // remember the handle
- globals->thisComponent.format = kOutputSampleFormat; // output sample format
- globals->thisComponent.sampleSize = kOutputSampleSize; // output sample size
- globals->outputSamples = kMaxOutputSamples; // size of our output buffer
-
- return (noErr);
- }
-
- /* ==============================================================================
- Component Close
-
- This routine is called when the Component Manager is closing the instance of
- this component. It should delete all global storage and close any other components
- that were opened.
-
- NOTE: Be sure to check that the globals pointer passed in to this routine is
- not set to NIL. If the Open() routine fails for any reason, the Component
- Manager will call this routine passing in a NIL for the globals.
- NOTE: This routine is never called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __ComponentClose(GlobalsPtr globals, ComponentInstance self)
- {
- #pragma unused (self)
-
- if (globals) // we have some globals
- {
- if (globals->sourceComponent) // we have a source component
- CloseComponent(globals->sourceComponent); // close it
-
- globals->thisComponent.sampleCount = 0; // nothing in our buffer now
- DisposeHandle(globals->globalsHandle); // dispose our storage
- }
-
- return (noErr);
- }
-
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Standard Decompression Component Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- /* ==============================================================================
- SetSource
-
- This routine sets the component we should call to get more data. We must remember
- this component and also tell it the data format of our component requires.
- ============================================================================== */
-
- pascal ComponentResult __SetSource(GlobalsPtr globals, SoundSource sourceID, ComponentInstance source)
- {
- #pragma unused (sourceID)
-
- SoundComponentDataPtr sourceSifter;
-
- globals->sourceComponent = source; // remember our source component
- globals->sourceDataPtr = nil; // nothing read from source yet
-
- // make sure we can get the source we need
- return (SoundComponentSetOutput(source, &globals->thisComponent, &sourceSifter));
- }
-
- /* ==============================================================================
- SetOutput
-
- This routine sets the data format our component should output. If we can't output
- the requested format, we should return a pointer to the format we do support,
- and return an error, and the Sound Manager will attempt the conversion for us.
- ============================================================================== */
-
- pascal ComponentResult __SetOutput(GlobalsPtr globals, SoundComponentDataPtr requested, SoundComponentDataPtr *actual)
- {
- globals->outputSamples = requested->sampleCount; // no. samples to output
- if (globals->outputSamples > kMaxOutputSamples) // too much for our buffer
- globals->outputSamples = kMaxOutputSamples; // only output what we can
-
- // make sure data format and sample sizes match
-
- if ((requested->format == kOutputSampleFormat) && // formats match
- (requested->sampleSize == kOutputSampleSize)) // sample sizes match
- {
- return (noErr); // no problem outputting this format
- }
- else
- {
- // If we can't output the requested format, the Sound Manager will make an attempt to convert our
- // format into something that can be used. In order for the Sound Manager to do this, we need to
- // tell it here the format we will be outputting, so it can setup the proper conversion. This is really
- // handy if your algorithm only outputs 16-bit data, but the Sound Manager is requesting 8-bit. In this
- // case, the Sound Manager will automatically convert your 16-bit data to 8-bit.
-
- *actual = &globals->thisComponent; // tell the Sound Manager what we will output
- return (paramErr); // force the Sound Manager to convert for us
- }
- }
-
- /* ==============================================================================
- GetInfo
-
- This routine returns information about this output component to the Sound Manager.
- A 4-byte OSType selector is used to determine the type and size of the information
- to return. If the component does not support a selector, it should delegate this
- call on up the chain.
-
- NOTE: This can be called at interrupt time. However, selectors that return
- a handle will not be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __GetInfo(GlobalsPtr globals, SoundSource sourceID,
- OSType selector, void *infoPtr)
- {
- ComponentResult result = noErr;
-
- switch (selector)
- {
- case siCompressionFactor:
- GetCompressorInfo(infoPtr); // fill out the CompressionInfo structure passed in
- break;
-
- default:
- result = SoundComponentGetInfo(globals->sourceComponent, sourceID, selector, infoPtr);
- break;
- }
-
- return (result);
- }
-
- /* ==============================================================================
- StopSource
-
- This routine is used to stop sounds that are currently playing. It should
- clear out any internal buffers, reset any decompression state information
- and then delegate the call up the chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __StopSource(GlobalsPtr globals, short count, SoundSource *sources)
- {
- globals->sourceDataPtr = nil; // clear out internal buffers
- globals->thisComponent.sampleCount = 0; // our buffer is empty
- InitializeDecompressor(globals); // initialize our decompressor state
-
- return (SoundComponentStopSource(globals->sourceComponent, count, sources)); // delegate this call
- }
-
- /* ==============================================================================
- PlaySourceBuffer
-
- This routine is used to start a new sound playing. It should clear out any internal buffers
- but should NOT reset any decompression state information, since this could be a
- continuation of a sound that has been broken into pieces. Then the call should be
- delegated up the chain.
-
- NOTE: This can be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __PlaySourceBuffer(GlobalsPtr globals, SoundSource sourceID, SoundParamBlockPtr pb, long actions)
- {
- globals->sourceDataPtr = nil; // clear out internal buffers
- globals->thisComponent.sampleCount = 0; // our buffer is empty
-
- return (SoundComponentPlaySourceBuffer(globals->sourceComponent, sourceID, pb, actions)); // delegate this call
- }
-
- /* ==============================================================================
- GetSourceData
-
- This routine is called when the Sound Manager wants your component to decompress
- some more data. It should first make sure it has some source data, then
- decompress into an internal buffer and return that buffer to the Sound Manager.
- If the Sound Manager is requesting the data in reverse, you must decompress
- the data starting from the end of the source buffer, and the Sound Manager will
- reverse the samples for you.
-
- NOTE: This will most often be called at interrupt time.
- ============================================================================== */
-
- pascal ComponentResult __GetSourceData(GlobalsPtr globals, SoundComponentDataPtr *resultDataPtr)
- {
- SoundComponentDataPtr sourceDataPtr;
- unsigned long samplesConverted, framesToConvert, bytesToSkip;
- ComponentResult result;
- Byte *inputBuffer;
-
- result = noErr;
- sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component
-
- if (sourceDataPtr == nil) // source buffer was flushed
- {
- result = PrimeSource(globals); // start with all new source data
- }
- else if (sourceDataPtr->sampleCount == 0) // source buffer is empty
- {
- result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr); // fill it up // continue where we left off
- }
-
- if (result != noErr) // bail if error
- return (result);
-
- sourceDataPtr = globals->sourceDataPtr; // get pointer to source sound component
-
- if ((sourceDataPtr->format == globals->thisComponent.format) || // input and output are same
- (sourceDataPtr->buffer == nil)) // or no source buffer
- {
- globals->sourceDataPtr = nil; // get new source next time
- *resultDataPtr = sourceDataPtr; // pass source on down
- }
- else
- {
- // convert the source samples into frames
- framesToConvert = sourceDataPtr->sampleCount / globals->compInfo.samplesPerPacket;
-
- if (framesToConvert) // source has some data for us
- {
- if (framesToConvert > globals->outputFrames)
- framesToConvert = globals->outputFrames; // limited to size of output
-
- // convert frames back into samples to quantize the source correctly
- samplesConverted = framesToConvert * globals->compInfo.samplesPerPacket;
-
- // If the Sound Manager is going to play this sound backwards, we need to decompress
- // starting from the end of the buffer, and it will reverse the decompressed samples for us.
-
- inputBuffer = sourceDataPtr->buffer; // point at input buffer
- if (globals->reverse)
- {
- bytesToSkip = sourceDataPtr->sampleCount - samplesConverted; // no. samples to skip
- bytesToSkip = (bytesToSkip / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame; // convert samples into bytes
- inputBuffer += bytesToSkip; // decompress from end of buffer
- }
-
- // If it is playing forward (the normal case), we just skip over the source samples that
- // will be decompressed below.
-
- else
- {
- bytesToSkip = (samplesConverted / globals->compInfo.samplesPerPacket) * globals->compInfo.bytesPerFrame;
- sourceDataPtr->buffer += bytesToSkip; // skip over the source consumed
- }
-
- sourceDataPtr->sampleCount -= samplesConverted; // this many samples will be decompressed
-
- // Do the decompression
-
- if (sourceDataPtr->numChannels == 1) // mono - decompress right into output buffer
- {
- DecompressULaw(inputBuffer, globals->buffer, framesToConvert, 1, 1);
- }
- else // stereo - decompress each channel separately
- {
- // do the left samples
- DecompressULaw(inputBuffer, globals->buffer, framesToConvert, 2, 1);
-
- // do the right samples
- DecompressULaw(inputBuffer, globals->buffer, framesToConvert, 2, 2);
- }
- }
- else
- samplesConverted = 0; // no samples were converted
-
- globals->thisComponent.buffer = (Byte *) globals->buffer; // data in this buffer
- globals->thisComponent.sampleCount = samplesConverted; // return num. samples converted
-
- *resultDataPtr = &globals->thisComponent; // return description of decompressed data
- }
-
- return (result);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine is used to prime the source. It gets the first load of data
- // from the source and intializes the output format and compression factors.
-
- ComponentResult PrimeSource(GlobalsPtr globals)
- {
- ComponentResult result;
- SoundComponentDataPtr sourceDataPtr;
-
- // get data from source
-
- result = SoundComponentGetSourceData(globals->sourceComponent, &globals->sourceDataPtr);
- if (result != noErr)
- return (result);
- if (globals->sourceDataPtr == nil)
- return (paramErr);
-
- // copy source settings so we know what to output. Notice that we do not change the sampleSize
- // or format fields, since we always output 16-bit, twos-complement data in this example.
-
- sourceDataPtr = globals->sourceDataPtr;
- globals->thisComponent.flags = sourceDataPtr->flags; // copy flags unchanged
- globals->thisComponent.sampleRate = sourceDataPtr->sampleRate; // copy sample rate unchanged
- globals->thisComponent.numChannels = sourceDataPtr->numChannels; // copy numchannels unchanged
-
- // The Sound Manager sets the flags field of the sourceDataPtr to indicate if the
- // sound should be played backwards. If this bit is set, we just store it in the
- // globals so we know to decompress from the end of the buffer.
-
- if (sourceDataPtr->flags & kReverse) // source is to be played backwards
- globals->reverse = true;
- else
- globals->reverse = false;
-
- // Setup the the compression info, so that we can convert between samples, frames and bytes correctly.
-
- globals->compInfo.recordSize = sizeof(CompressionInfo);
- globals->compInfo.format = sourceDataPtr->format;
- GetCompressorInfo(&globals->compInfo); // get compression info
- globals->compInfo.bytesPerFrame = globals->compInfo.bytesPerPacket * sourceDataPtr->numChannels;
-
- globals->outputFrames = globals->outputSamples / globals->compInfo.samplesPerPacket;
-
- return (noErr);
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Compressor-specific Methods
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine initializes the decompressor.
-
- void InitializeDecompressor(GlobalsPtr globals) // initialize our decompressor state
- {
- #pragma unused (globals)
-
- // Here you should initialize any state used by your decompression alorithm (such as predictors)
- // to default values. This routine will be called whenever a new sound is started so you can set
- // up correctly. In our example, we do not have any state, so we have nothing to set up.
- }
-
- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // This routine returns information about the compression ratios.
-
- void GetCompressorInfo(CompressionInfoPtr cp)
- {
- if (cp->recordSize > sizeof(CompressionInfo)) // limit amount we return
- cp->recordSize = sizeof(CompressionInfo);
-
- cp->compressionID = fixedCompression; // must set this to fixedCompression
- cp->samplesPerPacket = 1; // no. samples in one compressed packet
- cp->bytesPerPacket = 1; // no. bytes in a packet
- cp->bytesPerSample = 2; // no. bytes in a decompressed sample
- }
-
- /* This example uses uLaw encoding.
-
- In uLaw, 14-bits of linear sampling is reduced to 8 bits of logarithmic data.
- It is used on North American and Japanese phone systems, and is coming into use
- for voice data interchange, for PBXs, voicemail, MIME (Internet standard for multimedia mail),
- and Internet Talk Radio. µLaw sounds are almost always encoded at 8000, 8012, or 8012.8210513
- samples/sec. 8000 because it is the specified standard for phones, and 8012.8210513
- (and rounded to 8012) because it is apparently the actual rate used in domestic digital phone switches.
-
- ---------------------------
- U-LAW and A-LAW definitions
- ---------------------------
-
- [Adapted from information provided by duggan@cc.gatech.edu (Rick
- Duggan) and davep@zenobia.phys.unsw.EDU.AU (David Perry)]
-
- u-LAW (really mu-LAW) is
-
- sgn(m) ( |m |) |m |
- y= ------- ln( 1+ u|--|) |--| =< 1
- ln(1+u) ( |mp|) |mp|
-
- A-LAW is
-
- | A (m ) |m | 1
- | ------- (--) |--| =< -
- | 1+ln A (mp) |mp| A
- y=|
- | sgn(m) ( |m |) 1 |m |
- | ------ ( 1+ ln A|--|) - =< |--| =< 1
- | 1+ln A ( |mp|) A |mp|
-
- Values of u=100 and 255, A=87.6, mp is the Peak message value, m is the current quantised message value.
- (The formulae get simpler if you substitute x for m/mp and sgn(x) for sgn(m); then -1 <= x <= 1.)
-
- Converting from u-LAW to A-LAW is in a sense "lossy" since there are quantizing errors introduced in
- the conversion.
-
- "..the u-LAW used in North America and Japan, and the A-LAW used in Europe and the rest of the world
- and international routes.."
-
- References:
-
- Modern Digital and Analog Communication Systems, B.P.Lathi., 2nd ed. ISBN 0-03-027933-X
-
- Transmission Systems for Communications
- Fifth Edition
- by Members of the Technical Staff at Bell Telephone Laboratories
- Bell Telephone Laboratories, Incorporated
- Copyright 1959, 1964, 1970, 1982
- */
-
- /*
- ** This routine converts from ulaw to 16 bit linear. The really fast way to do this is
- ** with a table, but this is left as an exercise for the reader.
- **
- ** Craig Reese: IDA/Supercomputing Research Center
- ** 29 September 1989
- **
- ** References:
- ** 1) CCITT Recommendation G.711 (very difficult to follow)
- ** 2) MIL-STD-188-113,"Interoperability and Performance Standards
- ** for Analog-to_Digital Conversion Techniques,"
- ** 17 February 1987
- **
- ** Input: 8 bit ulaw sample
- ** Output: signed 16 bit linear sample
- */
-
- void DecompressULaw(Byte *inbuf, short *outbuf, unsigned long framesToConvert,
- unsigned long numChannels, unsigned long whichChannel)
- {
- const int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
- unsigned char ulawbyte;
- int i, sign, exponent, mantissa, sample;
-
- whichChannel--;
- inbuf += whichChannel;
- outbuf += whichChannel;
-
- for (i = 0; i < framesToConvert; i++) // loop over all source frames
- {
- ulawbyte = *inbuf; // get µLaw sample
- inbuf += numChannels;
-
- ulawbyte = ~ulawbyte; // convert µLaw sample to 16-bit linear sample
- sign = (ulawbyte & 0x80);
- exponent = (ulawbyte >> 4) & 0x07;
- mantissa = ulawbyte & 0x0F;
- sample = exp_lut[exponent] + (mantissa << (exponent + 3));
- if (sign != 0)
- sample = -sample;
-
- *outbuf = sample; // output 16-bit sample
- outbuf += numChannels;
- }
- }
-